from string import ascii_letters

IDENT_CHRS = ascii_letters + '_'
IDENT_CHRS_AFTER = IDENT_CHRS + '0123456789'

keywords = ['void', 'int', 'while', 'if', 'else', 'return', 'break', 'continue', 'print', 'readint']

TT_PLUS = 'PLUS' # +
TT_MINUS = 'MINUS' # -
TT_MUL = 'MUL' # *
TT_DIV = 'DIV' # /
TT_MOD = 'MOD' # %
TT_EQ = 'EQ' # =
TT_COMMA = 'COMMA' # ,
TT_SEMICOLON = 'SEMICOLON' # ;
TT_EXCL = 'EXCL' # !
TT_LT = 'LT' # <
TT_GT = 'GT' # >
TT_LPAREN = 'LPAREN' # (
TT_RPAREN = 'RPAREN' # )
TT_LBRACE = 'LBRACE' # {
TT_RBRACE = 'RBRACE' # }

TT_LE = 'LE' # <=
TT_GE = 'GE' # >=
TT_EE = 'EE' # ==
TT_NE = 'NE' # !=
TT_LAND = 'LAND' # &&
TT_LOR = 'LOR' # ||

TT_NUMBER = 'NUMBER'
TT_STRING = 'STRING'
TT_IDENTIFIER = 'IDENTIFIER'
TT_KEYWORD = 'KEYWORD'
TT_EOF = 'EOF'

class Token:
    def __init__(self, type_, value=None):
        self.type = type_
        self.value = value
    
    def matches(self, type_, value):
        return self.type == type_ and self.value == value
    
    def __repr__(self):
        if self.value: return f'{self.type}:{self.value}'
        else: return f'{self.type}'

class Lexer:
    def __init__(self, text):
        self.text = text
        self.pos = -1
        self.current_char = None
        self.advance()
    
    def advance(self):
        self.pos += 1
        self.current_char = self.text[self.pos] if self.pos < len(self.text) else None
    
    def make_tokens(self):
        tokens = []

        while self.current_char:
            if self.current_char in ' \t\n':
                self.advance()
            elif self.current_char in '0123456789':
                tokens.append(self.make_digits())
            elif self.current_char in IDENT_CHRS:
                tokens.append(self.make_identifier())
            elif self.current_char == '"':
                tokens.append(self.make_string())
            elif self.current_char == '+':
                tokens.append(Token(TT_PLUS))
                self.advance()
            elif self.current_char == '-':
                tokens.append(Token(TT_MINUS))
                self.advance()
            elif self.current_char == '*':
                tokens.append(Token(TT_MUL))
                self.advance()
            elif self.current_char == '/':
                self.advance()
                if self.current_char == '/':
                    self.skip_comments()
                else:
                    tokens.append(Token(TT_DIV))
            elif self.current_char == '(':
                tokens.append(Token(TT_LPAREN))
                self.advance()
            elif self.current_char == ')':
                tokens.append(Token(TT_RPAREN))
                self.advance()
            elif self.current_char == ',':
                tokens.append(Token(TT_COMMA))
                self.advance()
            elif self.current_char == ';':
                tokens.append(Token(TT_SEMICOLON))
                self.advance()
            elif self.current_char == '{':
                tokens.append(Token(TT_LBRACE))
                self.advance()
            elif self.current_char == '}':
                tokens.append(Token(TT_RBRACE))
                self.advance()
            elif self.current_char == '<':
                tokens.append(self.make_less_than())
            elif self.current_char == '>':
                tokens.append(self.make_greater_than())
            elif self.current_char == '=':
                tokens.append(self.make_equals())
            elif self.current_char == '!':
                tokens.append(self.make_not_equals())
            elif self.current_char == '&':
                tokens.append(self.make_logical_and())
            elif self.current_char == '|':
                tokens.append(self.make_logical_or())
            elif self.current_char == '#':
                self.skip_comments()
            else:
                print("Illegal Character:", self.current_char)
                exit()
    
        tokens.append(Token(TT_EOF))
        return tokens

    def make_digits(self):
        num_str = ''
        while self.current_char and self.current_char in '0123456789':
            num_str += self.current_char
            self.advance()
        
        return Token(TT_NUMBER, int(num_str))
    
    def make_identifier(self):
        id_str = ''
        
        while self.current_char and self.current_char in IDENT_CHRS_AFTER:
            id_str += self.current_char
            self.advance()
        
        if id_str in keywords:
            return Token(TT_KEYWORD, id_str)
        else:
            return Token(TT_IDENTIFIER, id_str)
    
    def make_string(self):
        string = ''
        escape_character = False
        self.advance()

        escape_characters = {
            'n': '\n',
            't': '\t'
        }

        while self.current_char and (self.current_char != '"' or escape_character):
            if escape_character:
                string += escape_characters.get(self.current_char, self.current_char)
            else:
                if self.current_char == '\\':
                    escape_character = True
                else:
                    string += self.current_char
            self.advance()
            escape_character = False
        
        self.advance()
        return Token(TT_STRING, f'"{string}"')

    def make_less_than(self):
        tok_type = TT_LT
        self.advance()
        if self.current_char == '=':
            self.advance()
            tok_type = TT_LE
        return Token(tok_type)
    
    def make_greater_than(self):
        tok_type = TT_GT
        self.advance()
        if self.current_char == '=':
            self.advance()
            tok_type = TT_GE
        return Token(tok_type)
    
    def make_less_than(self):
        tok_type = TT_LT
        self.advance()
        if self.current_char == '=':
            self.advance()
            tok_type = TT_LE
        return Token(tok_type)
    
    def make_equals(self):
        tok_type = TT_EQ
        self.advance()
        if self.current_char == '=':
            self.advance()
            tok_type = TT_EE
        return Token(tok_type)
    
    def make_not_equals(self):
        tok_type = TT_EXCL
        self.advance()
        if self.current_char == '=':
            self.advance()
            tok_type = TT_LE
        return Token(tok_type)
    
    def make_logical_and(self):
        self.advance()
        if self.current_char == '&':
            self.advance()
            return Token(TT_LAND)
        print("Expected: '&' (after '&')")
        exit()
    
    def make_logical_or(self):
        self.advance()
        if self.current_char == '|':
            self.advance()
            return Token(TT_LOR)
        print("Expected: '|' (after '|')")
        exit()
    
    def skip_comments(self):
        while self.current_char != '\n':
            self.advance()

class Parser:
    def __init__(self, tokens):
        self.tokens = tokens
        self.tok_idx = -1
        self.advance()
        self.ir = ""
        self.macro = ""
        self.if_stack = []
        self.while_stack = []
        self.if_cnt = 0
        self.while_cnt = 0
        self.codes = {TT_PLUS: 'add', TT_MINUS: 'sub', TT_MUL: 'mul', TT_DIV: 'div', TT_MOD: 'mod',
                TT_LT: 'cmplt', TT_LE: 'cmple', TT_GT: 'cmpgt', TT_GE: 'cmpge', TT_EE: 'cmpeq', TT_NE: 'cmpne',
                TT_LAND: 'and', TT_LOR: 'or'}
        self.cur_func_args = []
        self.cur_func_vars = []
    
    def advance(self):
        self.tok_idx += 1
        if self.tok_idx < len(self.tokens):
            self.current_tok = self.tokens[self.tok_idx]
    
    def parse(self):
        error = self.program()
        return self.ir, self.macro, error

    def program(self):
        while True:
            if self.current_tok.type == TT_EOF:
                self.advance()
                break
            error = self.func_decl()
            if error: return error
    
    def func_decl(self):
        # 希望遇到一个int或void
        if (not self.current_tok.matches(TT_KEYWORD, 'int')) and (not self.current_tok.matches(TT_KEYWORD, 'void')):
            return "Illegal return type"
        self.advance()
        if not self.current_tok.type == TT_IDENTIFIER:
            return "Expected IDENTIFIER after int/void in function"
        funcname = self.current_tok.value
        self.ir += f"FUNC @{funcname}:\n"
        self.advance()
        if not self.current_tok.type == TT_LPAREN:
            return "Expected '(' after function name"
        self.advance()
        if self.current_tok.matches(TT_KEYWORD, 'int'):
            self.advance()
            if self.current_tok.type != TT_IDENTIFIER:
                return "Expected arg name after 'int'"
            self.ir += f'\t{funcname}.arg {self.current_tok.value}'
            self.cur_func_args.append(self.current_tok.value)
            self.advance()

            while self.current_tok.type == TT_COMMA:
                self.advance()
                if not self.current_tok.matches(TT_KEYWORD, 'int'):
                    return "Expected 'int' after ','"
                self.advance()
                if self.current_tok.type != TT_IDENTIFIER:
                    return "Expected arg name after 'int'"
                self.ir += f', {self.current_tok.value}'
                self.cur_func_args.append(self.current_tok.value)
                self.advance()
        
            self.ir += "\n"
        
        if self.current_tok.type != TT_RPAREN:
            return "Expected ')' after parameters or '('"
        self.advance()
        if self.current_tok.type != TT_LBRACE:
            return "Expected '{' after ')'"
        self.advance()
        error = self.var_decls(funcname)
        if error: return error
        self.macro += f"; ==== begin function '{funcname}' ===="
        self.macro += f"\n%define {funcname}.argc {len(self.cur_func_args)}"
        self.macro += f"\n%macro ${funcname} 0"
        self.macro += f"\n    CALL @{funcname}"
        self.macro += f"\n    ADD ESP, 4*{funcname}.argc"
        self.macro += f"\n    PUSH EAX"
        self.macro += f"\n%endmacro\n"
        if len(self.cur_func_args):
            self.macro += f"\n%macro {funcname}.arg {funcname}.argc"
            for i in range(len(self.cur_func_args)):
                self.macro += f"\n    %define {self.cur_func_args[i]} [EBP + 8 + 4*{funcname}.argc - 4*{i + 1}]"
            self.macro += f"\n%endmacro\n"
        if len(self.cur_func_vars):
            self.macro += f"\n%define {funcname}.varc {len(self.cur_func_vars)}"
            self.macro += f"\n%macro {funcname}.var {funcname}.varc"
            for i in range(len(self.cur_func_vars)):
                self.macro += f"\n    %define {self.cur_func_vars[i]} [EBP - 4*{i + 1}]"
            self.macro += f"\n    SUB ESP, 4*{funcname}.varc"
            self.macro += f"\n%endmacro\n"
        error = self.stmts()
        if error: return error
        self.ir += f"ENDFUNC@{funcname}\n\n"
        if self.current_tok.type != TT_RBRACE:
            return "Expected '}' after function body"
        self.advance()
        self.macro += f"\n%macro ENDFUNC@{funcname} 0"
        self.macro += f"\n    LEAVE"
        self.macro += f"\n    RET"
        for arg in self.cur_func_args:
            self.macro += f"\n    %undef {arg}"
        for var in self.cur_func_vars:
            self.macro += f"\n    %undef {var}"
        self.macro += f"\n%endmacro\n"
        self.macro += f"\n; ==== end function '{funcname}' ==== \n\n"
        self.cur_func_args = []
        self.cur_func_vars = []

    def var_decls(self, funcname):
        while self.current_tok.matches(TT_KEYWORD, 'int'):
            error = self.var_decl(funcname)
            if error: return error
            if self.current_tok.type != TT_SEMICOLON:
                return "Expected ';' after variable declaration"
            self.advance()
    
    def var_decl(self, funcname):
        self.advance()
        if self.current_tok.type != TT_IDENTIFIER:
            return "Expected variable name after 'int'"
        self.ir += f'\t{funcname}.var {self.current_tok.value}'
        self.cur_func_vars.append(self.current_tok.value)
        self.advance()
        while self.current_tok.type == TT_COMMA:
            self.advance()
            if self.current_tok.type != TT_IDENTIFIER:
                return "Expected variable name after ','"
            self.ir += f', {self.current_tok.value}'
            self.cur_func_vars.append(self.current_tok.value)
            self.advance()
        self.ir += '\n'
    
    def stmts(self):
        while True:
            if self.current_tok.type == TT_IDENTIFIER:
                # assign/call
                name = self.current_tok.value
                self.advance()
                if self.current_tok.type == TT_EQ:
                    # assign
                    error = self.assign_stmt(name)
                    if error: return error
                elif self.current_tok.type == TT_LPAREN:
                    # call
                    error = self.call_stmt(name)
                    if error: return error
                else:
                    return "Expected '=' or '(' after variable name"
            elif self.current_tok.matches(TT_KEYWORD, 'print'):
                # print
                error = self.print_stmt()
                if error: return error
            elif self.current_tok.matches(TT_KEYWORD, 'return'):
                # return
                error = self.return_stmt()
                if error: return error
            elif self.current_tok.matches(TT_KEYWORD, 'continue'):
                # continue
                self.advance()
                if self.current_tok.type != TT_SEMICOLON:
                    return "Expected ';' after 'continue'"
                self.advance()
                self.ir += f'\tjmp _begWhile_{self.while_stack[-1]}\n'
            elif self.current_tok.matches(TT_KEYWORD, 'break'):
                # break
                self.advance()
                if self.current_tok.type != TT_SEMICOLON:
                    return "Expected ';' after 'break'"
                self.advance()
                self.ir += f'\tjmp _endWhile_{self.while_stack[-1]}\n'
            elif self.current_tok.matches(TT_KEYWORD, 'if'):
                # if
                error = self.if_stmt()
                if error: return error
            elif self.current_tok.matches(TT_KEYWORD, 'while'):
                # while
                error = self.while_stmt()
                if error: return error
            else:
                break

    def assign_stmt(self, name):
        self.advance()
        error = self.expr()
        if error: return error
        if self.current_tok.type != TT_SEMICOLON:
            return "Expected ';' after assign statement"
        self.advance()
        self.ir += f'\tpop {name}\n'

    def call_expr(self, name):
        self.advance()
        if self.current_tok.type != TT_RPAREN:
            error = self.expr()
            if error: return expr
            while self.current_tok.type == TT_COMMA:
                self.advance()
                error = self.expr()
                if error: return error
            if self.current_tok.type != TT_RPAREN:
                return "Expected ')' after function call"
        self.advance()
        self.ir += f'\t${name}\n'
    
    def call_stmt(self, name):
        error = self.call_expr(name)
        if error: return error
        if self.current_tok.type != TT_SEMICOLON:
            return "Expected ';' after statement"
        self.advance()
        self.ir += '\tpop\n'
    
    def print_stmt(self):
        self.advance()
        if self.current_tok.type != TT_LPAREN:
            return "Expected '(' after 'print'"
        self.advance()
        if self.current_tok.type != TT_STRING:
            return "Expected format string after '('"
        fmt = self.current_tok.value
        self.advance()
        while self.current_tok.type == TT_COMMA:
            self.advance()
            error = self.expr()
            if error: return error
        if self.current_tok.type != TT_RPAREN:
            return "Expected ')' after params"
        self.advance()
        if self.current_tok.type != TT_SEMICOLON:
            return "Expected ';' at the end of statement"
        self.advance()
        self.ir += f'\tprint {fmt}\n'
    
    def return_stmt(self):
        self.advance()
        if self.current_tok.type == TT_SEMICOLON:
            self.ir += f'\tret\n'
        else:
            error = self.expr()
            if error: return error
            if self.current_tok.type != TT_SEMICOLON:
                return "Expected ';' at the end of statement"
            self.ir += f'\tret ~\n'
        self.advance()
    
    def while_stmt(self):
        self.advance()
        self.while_cnt += 1
        self.while_stack.append(self.while_cnt)
        self.ir += f'_begWhile_{self.while_stack[-1]}:\n'
        if self.current_tok.type != TT_LPAREN:
            return "Expected '(' after 'while'"
        self.advance()
        error = self.expr()
        if error: return error
        if self.current_tok.type != TT_RPAREN:
            return "Expected ')' after test expr"
        self.advance()
        self.ir += f'\tjz _endWhile_{self.while_stack[-1]}\n'
        if self.current_tok.type != TT_LBRACE:
            return "Expected '{' after ')'"
        self.advance()
        error = self.stmts()
        if error: return error
        if self.current_tok.type != TT_RBRACE:
            return "Expected '}' after while body"
        self.advance()
        self.ir += f'\tjmp _begWhile_{self.while_stack[-1]}\n_endWhile_{self.while_stack[-1]}:\n\n'
        self.while_stack.pop()
    
    def if_stmt(self):
        self.advance()
        self.if_cnt += 1
        self.if_stack.append(self.if_cnt)
        self.ir += f'_begIf_{self.if_stack[-1]}:\n'
        if self.current_tok.type != TT_LPAREN:
            return "Expected '(' after 'if'"
        self.advance()
        error = self.expr()
        if error: return error
        if self.current_tok.type != TT_RPAREN:
            return "Expected ')' after test expr"
        self.advance()
        self.ir += f'\tjz _elIf_{self.if_stack[-1]}\n'
        if self.current_tok.type != TT_LBRACE:
            return "Expected '{' after ')'"
        self.advance()
        error = self.stmts()
        if error: return error
        if self.current_tok.type != TT_RBRACE:
            return "Expected '}' after if body"
        self.advance()
        self.ir += f'\tjmp _endIf_{self.if_stack[-1]}\n_elIf_{self.if_stack[-1]}:\n'
        if self.current_tok.matches(TT_KEYWORD, 'else'):
            self.advance()
            if self.current_tok.type != TT_LBRACE:
                return "Expected '{' after 'else'"
            self.advance()
            error = self.stmts()
            if error: return error
            if self.current_tok.type != TT_RBRACE:
                return "Expected '}' after else body"
            self.advance()
        self.ir += f'_endIf_{self.if_stack[-1]}:\n'
        self.if_stack.pop()

    def atom(self):
        if self.current_tok.type == TT_NUMBER:
            self.ir += f'\tpush {self.current_tok.value}\n'
            self.advance()
        elif self.current_tok.matches(TT_KEYWORD, 'readint'):
            # readint
            self.advance()
            if self.current_tok.type != TT_LPAREN:
                return "Expected '(' after 'readint'"
            self.advance()
            if self.current_tok.type != TT_STRING:
                return "Expected prompt in readint"
            prompt = self.current_tok.value
            self.advance()
            if self.current_tok.type != TT_RPAREN:
                return "Expected ')' after prompt"
            self.advance()
            self.ir += f'\treadint {prompt}\n'
        elif self.current_tok.type == TT_EXCL or self.current_tok.type == TT_MINUS:
            code = '\tneg\n' if self.current_tok.type == TT_MINUS else '\tnot\n'
            self.advance()
            error = self.expr()
            if error: return error
            self.ir += code
        elif self.current_tok.type == TT_IDENTIFIER:
            name = self.current_tok.value
            self.advance()
            if self.current_tok.type == TT_LPAREN:
                error = self.call_expr(name)
                if error: return error
                return None
            self.ir += f'\tpush {name}\n'
        elif self.current_tok.type == TT_LPAREN:
            self.advance()
            error = self.expr()
            if error: return error
            if self.current_tok.type != TT_RPAREN:
                return "No ')' after '('"
            self.advance()
    
    def arith_mul_div_mod(self):
        return self.bin_op(self.atom, (TT_MUL, TT_DIV, TT_MOD))
    
    def arith_add_sub(self):
        return self.bin_op(self.arith_mul_div_mod, (TT_PLUS, TT_MINUS))
    
    def cmp_without_eq_ne(self):
        return self.bin_op(self.arith_add_sub, (TT_LT, TT_LE, TT_GT, TT_GE))
    
    def cmp_eq_ne(self):
        return self.bin_op(self.cmp_without_eq_ne, (TT_EE, TT_NE))
    
    def logical_and(self):
        return self.bin_op(self.cmp_eq_ne, (TT_LAND,))
    
    def logical_or(self):
        return self.bin_op(self.logical_and, (TT_LOR,))
    
    def expr(self):
        return self.logical_or()

    def bin_op(self, func, ops):
        left = func()
        if left: return left
        while self.current_tok.type in ops:
            op_tok = self.current_tok
            self.advance()
            right = func()
            if right: return right
            self.ir += f'\t{self.codes[op_tok.type]}\n'

general_macro = '''
; ==== general macro for pcode ====
%macro print 1
    [SECTION .data]
        %%STRING: DB %1, 0
    [SECTION .text]
        PUSH DWORD %%STRING
        CALL _PRINT
        SHL EAX, 2
        ADD ESP, EAX
%endmacro

%macro readint 1
    [SECTION .data]
        %%STRING: DB %1, 0
    [SECTION .text]
        PUSH DWORD %%STRING
        CALL _READINT
        MOV [ESP], EAX
%endmacro

%macro exit 1
    PUSH DWORD %1
    CALL _exit
%endmacro

%macro add 0
    POP EAX
    ADD DWORD [ESP], EAX
%endmacro

%macro sub 0
    POP EAX
    SUB DWORD [ESP], EAX
%endmacro

%macro mul 0
    POP EAX
    MUL DWORD [ESP]
    MOV [ESP], EAX
%endmacro

%macro div 0
    XOR EDX, EDX
    POP EBX
    POP EAX
    DIV EBX
    PUSH EAX
%endmacro

%macro mod 0
    XOR EDX, EDX
    POP EBX
    POP EAX
    DIV EBX
    PUSH EDX
%endmacro

%macro neg 0
    NEG DWORD [ESP]
%endmacro

%macro cmpeq 0
    MOV EAX, [ESP + 4]
    CMP EAX, [ESP]
    PUSHF
    POP EAX
    SHR EAX, 6
    AND EAX, 0x1
    ADD ESP, 4
    MOV [ESP], EAX
%endmacro

%macro cmpne 0
    MOV EAX, [ESP + 4]
    CMP EAX, [ESP]
    PUSHF
    POP EAX
    SHR EAX, 6
    AND EAX, 0x1
    XOR EAX, 0x1
    ADD ESP, 4
    MOV [ESP], EAX
%endmacro

%macro cmpge 0
    MOV EAX, [ESP + 4]
    CMP EAX, [ESP]
    PUSHF
    POP EAX
    SHR EAX, 7
    AND EAX, 0x1
    XOR EAX, 0x1
    ADD ESP, 4
    MOV [ESP], EAX
%endmacro

%macro cmplt 0
    MOV EAX, [ESP + 4]
    CMP EAX, [ESP]
    PUSHF
    POP EAX
    SHR EAX, 7
    AND EAX, 0x1
    ADD ESP, 4
    MOV [ESP], EAX
%endmacro

%macro cmpgt 0
    POP EAX
    CMP EAX, [ESP]
    PUSHF
    POP EAX
    SHR EAX, 7
    AND EAX, 0x1
    MOV [ESP], EAX
%endmacro

%macro cmple 0
    POP EAX
    CMP EAX, [ESP]
    PUSHF
    POP EAX
    SHR EAX, 7
    AND EAX, 0x1
    XOR EAX, 0x1
    MOV [ESP], EAX
%endmacro

%macro and 0
    POP EAX
    AND EAX, [ESP]
    PUSHF
    POP EAX
    SHR EAX, 6
    AND EAX, 0x1
    XOR EAX, 0x1
    MOV [ESP], EAX
%endmacro

%macro or 0
    POP EAX
    OR EAX, [ESP]
    PUSHF
    POP EAX
    SHR EAX, 6
    AND EAX, 0x1
    XOR EAX, 0x1
    MOV [ESP], EAX
%endmacro

%macro not 0
    MOV EAX, [ESP]
    OR EAX, EAX
    PUSHF
    POP EAX
    SHR EAX, 6
    AND EAX, 0x1
    MOV [ESP], EAX
%endmacro

%macro jz 1
    POP EAX
    OR EAX, EAX
    JZ %1
%endmacro

%macro jmp 1
    JMP %1
%endmacro

%macro push 1
    PUSH DWORD %1
%endmacro

%macro pop 0-1
    %ifidn %0, 0
        ADD ESP, 4
    %else
        POP DWORD %1
    %endif
%endmacro

%macro FUNC 1
    %1
    PUSH EBP
    MOV EBP, ESP
%endmacro

%macro ret 0-1
    %ifidn %0, 1
        %ifidn %1, ~
            MOV EAX, [ESP]
        %else
            MOV EAX, %1
        %endif
    %endif
    LEAVE
    RET
%endmacro

EXTERN _PRINT, _READINT
EXTERN _exit

GLOBAL _WinMain@16, _start
_WinMain@16:
_start:
    CALL @main
    PUSH EAX
    exit [ESP]
'''

myccio_c = '''
#include <stdio.h>

#define BUFLEN 1024

int PRINT(char *fmt, ...)
{
    int *args = (int*)&fmt;
    char buf[BUFLEN];
    char *p1 = fmt, *p2 = buf + BUFLEN;
    int len = -1, argc = 1;

    while (*p1++) ;

    do {
        p1--;
        if (*p1 == '%' && *(p1+1) == 'd') {
            p2++; len--; argc++;
            int num = *(++args), negative = 0;

            if (num < 0) {
                negative = 1;
                num = -num;
            }

            do {
                *(--p2) = num % 10 + '0'; len++;
                num /= 10;
            } while (num);

            if (negative) {
                *(--p2) = '-'; len++;
            }
        } else {
            *(--p2) = *p1; len++;
        }
    } while (p1 != fmt);

    puts(p2);

    return argc;
}

int READINT(char *prompt)
{
    printf("%s", prompt);
    int retval;
    scanf("%d", &retval);
    return retval;
}
'''

def run(filename):
    import os
    import os.path as op
    name, ext = op.splitext(filename)
    with open(filename, 'r') as f:
        text = f.read()
    lexer = Lexer(text)
    tokens = lexer.make_tokens()
    parser = Parser(tokens)
    ir, macro, error = parser.parse()
    if error:
        print(error)
        exit()
    with open(name + '.asm', 'w') as f:
        f.write(ir)
    with open(name + '.inc', 'w') as f:
        f.write(macro)
        f.write(general_macro)
    if os.name == "nt":
        status = os.system(f"nasm -f win32 -P\"{name + '.inc'}\" {name + '.asm'} -o {name + '.o'}")
        if status:
            print("No nasm in this computer or error in executing. Compilation terminated.")
            exit()
        if not op.exists("myccio.o"):
            with open("myccio.c", "w") as f:
                f.write(myccio_c)
            status = os.system(f"gcc -m32 -c myccio.c -o myccio.o -fleading-underscore")
            if status:
                print("Error compiling myccio. Compilation terminated.")
                exit()
        status = os.system(f"gcc -m32 myccio.o {name + '.o'} -o {name} -fleading-underscore")
        if status:
            print("Error linking final binary. Compilation terminated.")
            exit()
        os.system(f"del /f /s /q {name + '.o'} {name + '.asm'} {name + '.inc'} myccio.c myccio.o > nul")
    else:
        status = os.system(f"nasm -f elf32 -P\"{name + '.inc'}\" {name + '.asm'} -o {name + '.o'}")
        if status:
            print("No nasm in this computer or error in executing. Compilation terminated.")
            exit()
        if not op.exists("myccio.o"):
            with open("myccio.c", "w") as f:
                f.write(myccio_c)
            status = os.system(f"gcc -m32 -c myccio.c -o myccio.o -fleading-underscore")
            if status:
                print("Error compiling myccio. Compilation terminated.")
                exit()
        status = os.system(f"gcc -m32 myccio.o {name + '.o'} -o {name} -fleading-underscore")
        if status:
            print("Error linking final binary. Compilation terminated.")
            exit()
        os.system(f"rm -f {name + '.o'} {name + '.asm'} {name + '.inc'} myccio.c myccio.o > /dev/null")

if __name__ == '__main__':
    import sys
    run(sys.argv[1])